import React from 'react';
import mapValues from 'lodash/mapValues';

import {Tabs as CTabs, Tab} from '../components/Tabs';
import {Renderer, RendererProps} from '../factory';
import {resolveVariable} from '../utils/tpl-builtin';
import {str2AsyncFunction} from '../utils/api';
import {
    isVisible,
    autobind,
    isDisabled,
    isObject,
    createObject
} from '../utils/helper';

import {filter} from '../utils/tpl';
import {SchemaTpl, SchemaClassName, BaseSchema, SchemaCollection, SchemaIcon} from '../Schema';

import {ActionSchema} from './Action';

/**
 * 栏目容器渲染器。
 * 文档：https://baidu.gitee.io/amis/docs/components/portlet
 */
export interface PortletTabSchema extends Omit<BaseSchema, 'type'> {
    /**
     * Tab 标题
     */
    title?: string;
  
    /**
     * 内容
     * @deprecated 用 body 属性
     */
    tab?: SchemaCollection;

    /**
     * 可以在右侧配置点其他功能按钮，随着tab切换而切换
     */
    toolbar?: Array<ActionSchema>;
  
    /**
     * 内容
     */
    body?: SchemaCollection;
  
    /**
     * 按钮图标
     */
    icon?: SchemaIcon;
  
    iconPosition?: 'left' | 'right';
  
    /**
     * 设置以后内容每次都会重新渲染
     */
    reload?: boolean;
  
    /**
     * 点开时才加载卡片内容
     */
    mountOnEnter?: boolean;
  
    /**
     * 卡片隐藏就销毁卡片节点。
     */
    unmountOnExit?: boolean;
}

export interface PortletSchema extends Omit<BaseSchema, 'type'> {
    /**
     * 指定为 portlet 类型
     */
    type: 'portlet';

    tabs: Array<PortletTabSchema>;
  
    /**
     * 关联已有数据，选项卡直接根据目标数据重复。
     */
    source?: string;
  
    /**
     * 类名
     */
    tabsClassName?: SchemaClassName;
  
    /**
     * 展示形式
     */
    tabsMode?: '' | 'line' | 'card' | 'radio' | 'vertical' | 'tiled';
  
    /**
     * 内容类名
     */
    contentClassName?: SchemaClassName;
  
    /**
     * 链接外层类名
     */
    linksClassName?: SchemaClassName;
  
    /**
     * 卡片是否只有在点开的时候加载？
     */
    mountOnEnter?: boolean;
  
    /**
     * 卡片隐藏的时候是否销毁卡片内容
     */
    unmountOnExit?: boolean;
  
    /**
     * 可以在右侧配置点其他功能按钮。不会随着tab切换
     */
    toolbar?: Array<ActionSchema>;
  
    /**
     * 是否支持溢出滚动
     */
    scrollable?: boolean;
    
    /**
     * header和内容是否展示分割线
     */
    divider?: boolean;

    /**
     * 标题右侧的描述
     */
    description?: SchemaTpl;

    /**
     * 影藏头部
     */
    hideHeader?: boolean;

    /**
     * 自定义样式
     */
    style?: string | {
        [propName: string]: any;
    };
}

export interface PortletProps
    extends RendererProps,
        Omit<PortletSchema, 'className' | 'contentClassName'>{   
    activeKey?: number;
    tabRender?: (tab: PortletTabSchema, props: PortletProps, index: number) => JSX.Element;
}

export interface PortletState {
    activeKey?: number;
}

export class Portlet extends React.Component<PortletProps, PortletState> {
    static defaultProps: Partial<PortletProps> = {
        className: '',
        mode: 'line',
        divider: true
    };
    renderTab?: (tab: PortletTabSchema, props: PortletProps, index: number) => JSX.Element;
    constructor(props: PortletProps) {
        super(props);

        const activeKey = props.activeKey || 0;
        
        this.state = {
            activeKey
        };
    }

    @autobind
    handleSelect(key: number) {
        const {onSelect, tabs} = this.props;
        if (typeof key === 'number' && key < tabs.length) {
            this.setState({
                activeKey: key
            });
        }
        
        if (typeof onSelect === 'string') {
            const selectFunc = str2AsyncFunction(onSelect, 'key', 'props');
            selectFunc && selectFunc(key, this.props);
        } else if (typeof onSelect === 'function') {
            onSelect(key, this.props);
        }
    }

    renderToolbarItem(toolbar: Array<ActionSchema>) {
        const {render} = this.props;
        let actions: Array<JSX.Element> = []
        if (Array.isArray(toolbar)) {
            toolbar.forEach((action, index) =>
                actions.push(
                    render(
                        `toolbar/${index}`,
                        {
                          type: 'button',
                          level: 'link',
                          size: 'sm',
                          ...(action as any)
                        },
                        {
                          key: index
                        }
                    )
                )
            );
        }
        return actions;
    }

    renderToolbar() {
        const {toolbar, classnames: cx, classPrefix: ns, tabs} = this.props;
        const activeKey = this.state.activeKey;
        let tabToolbar = null;
        let tabToolbarTpl = null;
        // tabs里的toolbar
        const toolbarTpl = toolbar ? (
            <div className={cx(`${ns}toolbar`)}>
              {this.renderToolbarItem(toolbar)}
            </div>
        ) : null;

        // tab里的toolbar
        if (typeof activeKey !== 'undefined') {
            tabToolbar = tabs[activeKey]!.toolbar;
            tabToolbarTpl = tabToolbar ? (
              <div className={cx(`${ns}tab-toolbar`)}>
                {this.renderToolbarItem(tabToolbar)}
              </div>
          ) : null;
        }

        return (
            toolbarTpl || tabToolbarTpl 
            ? (<div className={cx(`${ns}Portlet-toolbar`)}>
                {toolbarTpl}
                {tabToolbarTpl}
            </div>)
            : null
        );
    }

    renderDesc() {
        const {description : descTpl, render, classnames: cx, classPrefix: ns, data} = this.props;
        const desc = filter(descTpl, data);
        return desc
            ? <span className={cx(`${ns}Portlet-header-desc`)}>{desc}</span>
            : null;
    }
    
    renderTabs() {
        const {
            classnames: cx,
            classPrefix: ns,
            tabsClassName,
            contentClassName,
            linksClassName,
            tabRender,
            render,
            data,
            mode: dMode,
            tabsMode,
            unmountOnExit,
            source,
            mountOnEnter,
            scrollable,
            divider
        } = this.props;
        const mode = tabsMode || dMode;
        const arr = resolveVariable(source, data);
    
        let tabs = this.props.tabs;
        if (!tabs) {
          return null;
        }
    
        tabs = Array.isArray(tabs) ? tabs : [tabs];
        let children: Array<JSX.Element | null> = [];
        
        const tabClassname = cx(`${ns}Portlet-tab`, tabsClassName, {
          ['unactive-select']: tabs.length <=1,
          ['no-divider']: !divider 
        });
        if (Array.isArray(arr)) {
          arr.forEach((value, index) => {
            const ctx = createObject(
              data,
              isObject(value) ? {index, ...value} : {item: value, index}
            );
    
            children.push(
              ...tabs.map((tab, tabIndex) =>
                isVisible(tab, ctx) ? (
                  <Tab
                    {...(tab as any)}
                    title={filter(tab.title, ctx)}
                    disabled={isDisabled(tab, ctx)}
                    key={`${index * 1000 + tabIndex}`}
                    eventKey={index * 1000 + tabIndex}
                    mountOnEnter={mountOnEnter}
                    unmountOnExit={
                      typeof tab.reload === 'boolean'
                        ? tab.reload
                        : typeof tab.unmountOnExit === 'boolean'
                        ? tab.unmountOnExit
                        : unmountOnExit
                    }
                  >
                    {render(
                      `item/${index}/${tabIndex}`,
                      (tab as any)?.type ? (tab as any) : tab.tab || tab.body,
                      {
                        data: ctx
                      }
                    )}
                  </Tab>
                ) : null
              )
            );
          });
        } else {
          children = tabs.map((tab, index) =>
            isVisible(tab, data) ? (
              <Tab
                {...(tab as any)}
                title={filter(tab.title, data)}
                disabled={isDisabled(tab, data)}
                key={index}
                eventKey={index}
                mountOnEnter={mountOnEnter}
                unmountOnExit={
                  typeof tab.reload === 'boolean'
                    ? tab.reload
                    : typeof tab.unmountOnExit === 'boolean'
                    ? tab.unmountOnExit
                    : unmountOnExit
                }
              >
                {this.renderTab
                  ? this.renderTab(tab, this.props, index)
                  : tabRender
                  ? tabRender(tab, this.props, index)
                  : render(
                        `tab/${index}`,
                        (tab as any)?.type ? (tab as any) : tab.tab || tab.body
                    )}
              </Tab>
            ) : null
          );
        }
    
        return (
          <CTabs
            classPrefix={ns}
            classnames={cx}
            mode={mode}
            className={tabClassname}
            contentClassName={contentClassName}
            linksClassName={linksClassName}
            activeKey={this.state.activeKey}
            onSelect={this.handleSelect}
            toolbar={this.renderToolbar()}
            additionBtns={this.renderDesc()}
            scrollable={scrollable}
          >
            {children}
          </CTabs>
        );
    }
    
    render() {
        const {
            className,
            data,
            classnames: cx,
            classPrefix: ns,
            style,
            hideHeader
        } = this.props;
        const portletClassname = cx(`${ns}Portlet`, className, {
            ['no-header']: hideHeader 
        });
        const styleVar =
                typeof style === 'string'
                    ? resolveVariable(style, data) || {}
                    : mapValues(style, s => resolveVariable(s, data) || s);
        
        return (
            <div className={portletClassname} style={styleVar}>
                {this.renderTabs()}
            </div>
        )
    }
    
}

@Renderer({
  type: 'portlet'
})
export class PortletRenderer extends Portlet {
}
